package dm.ast.model.gen.factory;

import dm.sql.comp.ASTComplex;
import test.envset.GlobalState;
import test.envset.RunEnv;
import test.utility.Randomly;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public class BaseModelFactory {

    public static Randomly r = null;
    public static boolean allFlag = false; // 测试全部已支持的功能标志public static List<Integer> disabledIndexes = new ArrayList<Integer>();
    public static List<Integer> disabledIndexes = new ArrayList<>();

    public static int FLAG_HINT = 0;
    public static int FLAG_GROUP = 1;
    public static int FLAG_HAVING = 2;
    public static int FLAG_LIMIT = 3;
    public static int FLAG_ORDER = 4;
    public static int FLAG_WITH = 5;
    public static int FLAG_TOP = 6;
    public static int FLAG_GROUPBYCUBE = 7;
    public static int FLAG_GROUPBYROLLUP = 8;
    public static int FLAG_ROWNUM = 9;
    public static int FLAG_GROUPBYGROUPINGSETS = 10;
    public static int FLAG_UNION = 11;
    public static int FLAG_JOIN = 12;
    public static int FLAG_NATURALJOIN = 13;
    public static int FLAG_OUTERJOIN = 14;
    public static int FLAG_OLDOUTERJOIN = 15;
    public final static int FLAGS_CNT = 16;

    public boolean[] testFlags = new boolean[FLAGS_CNT];

    //查询谓词标志位，需要test.envset.RunEnv初始化
    //需要把rownum加上
    public static ArrayList<Boolean> runenvExpTypes = new ArrayList<Boolean>();
    public static int INDEX_ROWNUM = 0;
    public static int INDEX_NULL = 1;
    public static int INDEX_LIKE = 2;
    public static int INDEX_IN = 3;
    public static int INDEX_BETWEEN = 4;
    public static int INDEX_EXISTS = 5;
    public static int INDEX_LOGIC_ALL = 6;
    public static int INDEX_CASE = 7;
    public static int INDEX_SUBSELECT = 8;

    //默认不测试内部函数
    public static boolean ifunFlag = false;

    // 预设条件
    public static void setGlobalState(GlobalState global) {
        BaseModelFactory.r = global.getRandomly();
        BaseModelFactory.allFlag = GlobalState.getOptions().testAll();
        initDisabledFeatures(GlobalState.getOptions().getDisabledFeatures());
        initRunenvBoolTypes();
    }

    // -d禁止groupby子句/-d group
    //-d having 禁止having
    //-d order禁止orderby子句
    //-d limit禁止limit子句、offset子句
    //-d with禁止with子句
    //-d top禁止top子句
    //-d groupbycube 禁止groupby子句中的cube
    //-d group 禁止gnoupby子句
    //-d having禁止having//-d order禁止orderby子句
    //-d limit禁止limit子句、offset子句
    //-d with禁止with子句
    //-d top禁止top子句
    //-d groupbycube禁止groupby子句中的cube
    //-d groupbynollup 禁止groupby子句中的rollup
    private synchronized static void initDisabledFeatures(List<String> disabledFeatures) {
        if(disabledIndexes.size()==0 && disabledFeatures.size() != 0) {
            for (String feature : disabledFeatures) {
                if(feature.equalsIgnoreCase("correlated")){
                    //相关子查询单独处理
                    continue;
                }
                try {
                    Field field = BaseModelFactory.class.getDeclaredField("FLAG " + feature.toUpperCase());
                    int index = (int) field.get(null);
                    disabledIndexes.add(index);

                    //禁用join或者outerjoin时（+）也禁用
                    if(index == FLAG_JOIN || index == FLAG_OLDOUTERJOIN){
                        disabledIndexes.add(FLAG_OLDOUTERJOIN);
                    }
                } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
                    System.err.println("illegality disabled feature:" + feature);
                }
            }
        }
    }

    private synchronized static void initRunenvBoolTypes(){
        if(runenvExpTypes.size() == 0){
            runenvExpTypes.add(GlobalState.getOptions().testRownum());
            runenvExpTypes.add(RunEnv.NULL_FlAG);
            runenvExpTypes.add(RunEnv.LIKE_FLAG);
            runenvExpTypes.add(RunEnv.IN_FLAG);
            runenvExpTypes.add(RunEnv.BETWEEN_FLAG);
            runenvExpTypes.add(RunEnv.EXISTS_FLAG);
            runenvExpTypes.add(RunEnv.LOGIC_ALL_FLAG);
            runenvExpTypes.add(RunEnv.CASE_FLAG);
            runenvExpTypes.add(RunEnv.SUBSELECT_FLAG);

        }
    }

    //设置测试的功能点
    public void setTestFeatures() {
        //首先根据运行参数设置测试功能点
        setTestFeatureByOptions();
        setJoin();
        //如果设置-a(--test-a11),表示测试全部支持的功能,需要随机开启功能点
        if (allFlag) {
            //随机设置测试的功能点
            setRandTestFeatures();
            //关闭的特性
            disableFeature();
        }
    }

    //根据运行参数设置测试功能点
    private void setTestFeatureByOptions() {
        testFlags[FLAG_HINT] = GlobalState.getOptions().testHint();
        testFlags[FLAG_GROUP] = GlobalState.getOptions().testGroupby();
        testFlags[FLAG_HAVING] = GlobalState.getOptions().testHaving();
        testFlags[FLAG_LIMIT] = GlobalState.getOptions().testLimit();
        testFlags[FLAG_ORDER] = GlobalState.getOptions().testOrder();
        testFlags[FLAG_WITH] = GlobalState.getOptions().testWith();
        testFlags[FLAG_TOP] = GlobalState.getOptions().testTop();
        testFlags[FLAG_GROUPBYCUBE] = GlobalState.getOptions().testGroupbyCube();
        testFlags[FLAG_GROUPBYROLLUP] = GlobalState.getOptions().testGroupbyRollup();
        testFlags[FLAG_GROUPBYGROUPINGSETS] = GlobalState.getOptions().testGroupbyGroupingSets();
        testFlags[FLAG_ROWNUM] = GlobalState.getOptions().testRownum();
        testFlags[FLAG_UNION] = GlobalState.getOptions().testUnions();
        testFlags[FLAG_JOIN] = GlobalState.getOptions().testJoin();
        testFlags[FLAG_NATURALJOIN] = GlobalState.getOptions().testJoin();
        testFlags[FLAG_OUTERJOIN] = GlobalState.getOptions().testJoin();
        testFlags[FLAG_OLDOUTERJOIN] = GlobalState.getOptions().testJoin();
        ifunFlag = GlobalState.getOptions().testIFun();
    }

    //随机设置测试的功能点
    // 由于性能问题,不随机设置FLAG HINT
    private void setRandTestFeatures() {
        randEnableFlag(testFlags);
        if(testFlags[FLAG_HINT]){
            //当不测试hint时，hint开关关闭
            testFlags[FLAG_HINT] = GlobalState.getOptions().testHint();
        }
        if(Randomly.getBooleanWithSmallProbability()){
            ifunFlag = true;
        }
        setJoin();
    }

    private void setJoin(){
        if(testFlags[FLAG_JOIN]&&testFlags[FLAG_OLDOUTERJOIN]){
            if(Randomly.getBoolean()){//含有join
                testFlags[FLAG_OLDOUTERJOIN] = !testFlags[FLAG_JOIN];
            }else {//含有(+)
                testFlags[FLAG_JOIN] = !testFlags[FLAG_JOIN];
            }
        }
    }

    //-a测试时关闭的特性
    private void disableFeature() {
        for (int index : disabledIndexes) {
            testFlags[index] = false;
        }
    }

    private static void randEnableFlag(boolean[] flags){
        int i = 0;
        int cnt = flags.length;
        int set_cnt = Randomly.getInteger(0,cnt+1);
        if(set_cnt ==0){
            //不需要额外设置
            return;
        }
        if(set_cnt == (cnt - 1)){
            //所有功能点都设置
            for (i = 0; i < cnt; i++) {
                flags[i] = true;
            }
        }

        //随机测试需要测试的功能点
        ArrayList<Integer> tmpIndexes = new ArrayList<>();
        for (i = 0; i < cnt; i++) {
            tmpIndexes.add(i);
        }
        for (i = 0; i < set_cnt; i++) {
            //随即选择一个功能点设置为true
            int tmp = Randomly.fromList(tmpIndexes);
            flags[tmp] = true;
            //删除这个功能点索引，以免重复设置
            int j = 0;
            for (j = 0; j < tmpIndexes.size(); j++) {
                if(tmpIndexes.get(j) == tmp){
                    break;
                }
            }
            tmpIndexes.remove(j);
        }
    }

    //随机设置符合配置文件中查询谓词的标志位
    public static boolean[] getRandExpTypes(){
        boolean[] expTypes = new boolean[runenvExpTypes.size()];
        randEnableFlag(expTypes);
        //关闭禁止的标志位
        for (int i = 0; i < runenvExpTypes.size(); i++) {
            if(!runenvExpTypes.get(i)){
                expTypes[i] = false;
            }
        }
        //某些标志位不进行随即设置，以配置为主
        //由于谓词不适用子查询时导致深度主要是由from子句的多表连接决定，导致sql执行很慢
        expTypes[INDEX_SUBSELECT] = RunEnv.SUBSELECT_FLAG;
        return expTypes;
    }

    //结合深度估算最小的宽度
    public static int estimateMinWidt(int dep_t) {
        if (dep_t > 1) {
            return FactoryConstant.MIN_WIDT_COMP * dep_t;
        } else {
            return 1;
        }

    }

    //结合深度估算最小的复杂表达式
    public static int estimateMinCompWidt(int dep_t){
        if(dep_t > 1){
            return FactoryConstant.MIN_WIDT_COMP*dep_t;
        }else {
            return FactoryConstant.MIN_EXP_WIDT;
        }
    }

    //根据深度设置宽度
    public static int setExpWidtByDept(int dep_t){
        if(dep_t == 1){
            return Randomly.getInteger(1,FactoryConstant.MIN_EXP_WIDT);
        }else {
            return Randomly.getInteger(FactoryConstant.MIN_WIDT_COMP,FactoryConstant.MIN_WIDT_COMP*dep_t);
        }
    }

    //根据深度设置宽度
    public static int setWidtByDept(int dep_t) {
        if (dep_t == 1) {
            return Randomly.getInteger(1, FactoryConstant.MIN_WIDT_COMP);
        } else {
            return Randomly.getInteger(FactoryConstant.MIN_WIDT_COMP, FactoryConstant.MIN_WIDT_COMP * dep_t);
        }
    }

    //默认的分配方式:
    // 将总宽度,总深度分为指定数目的子部份。要求每部分的深度不超过总深度
    //@wid_t,总宽度
    // @dept,总深度
    // @fileds,分配子部分数量
    public static ComplexityModel defaultModel(int wid_t, int dep_t, int fields) {
        int tmp_widt_used = 0;
        if (fields == 0) {
            System.out.println("fields is 0!");
            throw new AssertionError();
        }
        if (dep_t < 0) {
            System.out.println("dep_t is " + dep_t);
            throw new AssertionError();
        }
        int tarwid = wid_t;
        if (tarwid < fields) {
            // 如果目标宽度过小,进行适度放大
            tarwid = fields;
        }
        ComplexityModel model = new ComplexityModel(fields);  //model包含 count = fields , comps[fields]
        if (tarwid > estimateMinWidt(dep_t)) {
            //宽度相对于深度来说足够
            // 每部分都可能设置为多深度
            int i = 0;
            for (i = 0; i < fields; i++) {
                model.comps[i].dep_t = Randomly.getInteger(1, dep_t + 1);
                model.comps[i].wid_t = setWidtByDept(model.comps[i].dep_t);
                tmp_widt_used += model.comps[i].wid_t;
                if (tmp_widt_used >= tarwid) {
                    // 宽度已经达到要求,那么终止
                    break;
                }
            }
            if (i != fields) {// 此时表示宽度已经使用完成,将后续部分的宽度和深度均设置为1
                // 还需要对comps打乱顺序
                for (; i < fields; i++) {
                    model.comps[i].wid_t = 1;
                    model.comps[i].dep_t = 1;
                }

                if (!GlobalState.getOptions().testUnions()){
                    List<ASTComplex> tmpComps = Arrays.asList(model.comps);
                    Collections.shuffle(tmpComps);
                    for (int ind = 0; ind < fields; ind++) {
                        model.comps[ind].wid_t = tmpComps.get(ind).wid_t;
                        model.comps[ind].dep_t = tmpComps.get(ind).dep_t;
                    }
                }

            } else { // 表示现在子部分已经分配完成,还需要检查宽度是否使用完成
                // 如果未使用完成,那么需要随机选择一个子部分增加宽
                int tmp = Randomly.getInteger(0, fields - 1);
                model.comps[tmp].wid_t += (tarwid - tmp_widt_used);
            }

            // 还需要检查最大深度是否达到要求
            // 如果未达到最大深度要求,那么将最大深度更改为要求深度,并修改对应宽度要求
            targetDept(model,dep_t);

        } else { // 宽度相对于深度不足
            // 随意选择其中一个,深度设为dept,宽度进行恰当设置
            // 其余部分,宽度设为1,深度设为1
            int j = Randomly.getInteger(0, fields);
            model.comps[j].dep_t = dep_t;
            model.comps[j].wid_t = setWidtByDept(dep_t);
            for (int i = 0; i < fields; i++) {
                if (i != j) {
                    model.comps[i].wid_t = 1;
                    model.comps[i].dep_t = 1;
                }
            }
        }
        return model;
    }

    // 检查最大深度是否达到要求
    //如果未达到最大深度要求,那么将最大深更改为要求深度,并修改对应宽度要求
    public static void targetDept(ComplexityModel model, int tarDept) {
        int ind = 0;
        int tmp_max_dept = 0;
        for (int i = 0; i < model.count; i++) {
            if (model.comps[i].dep_t > tmp_max_dept) {
                ind = i;
                tmp_max_dept = model.comps[i].dep_t;
            }
        }
        if (tmp_max_dept < tarDept) {
            model.comps[ind].dep_t = tarDept;
            if (estimateMinWidt(tarDept) > model.comps[ind].wid_t) {
                model.comps[ind].wid_t = setWidtByDept(tarDept);
            }
        }
    }

    //根据模板权重比例加权,随机返回一个模板
    public static ComplexityModel getRanComplexityModel(List<ComplexityModel> models) {
        int weights = 0;
        int i = 0;
        for (i = 0; i < models.size(); i++) {
            weights += models.get(i).weight;
        }
        int r_weight = Randomly.getInteger(1, weights + 1);  //这个函数(long)(getThreadRandom().get().longs(lower , upper).findFirst().getAsLong());获取一个随机数？
        weights = 0;
        for (i = 0; i < models.size(); i++) {
            weights += models.get(i).weight;
            if (weights >= r_weight) { // 找到第一个前面所有元素权重和大于目标权重的元素
                break;
            }
        }
        return models.get(i);
    }
}


				
				
	
	
	